#pragma once

#include<iostream>
#include<string>
#include<filesystem>
#include<fstream>
#include<ctime>
#include<memory>
#include<sstream>
#include"Mutex.hpp"

//日志等级
enum class LogLevel
{
    INFO,
    DEBUG,
    ERROR,
    WARNING,
    FATAL
};

//将日志等级转换为字符串
std::string Level2String(LogLevel level)
{
    switch(level)
    {
        case LogLevel::INFO:
            return "Info";
        case LogLevel::DEBUG:
            return "Debug";
        case LogLevel::ERROR:
            return "Error";
        case LogLevel::WARNING:
            return "warning";
        case LogLevel::FATAL:
            return "Fatal";
        default:
            return "Unknown";
    }
}

//刷新策略 -> 采用继承实现多态
class LogStrategy
{
public:
    virtual ~LogStrategy() = default;//默认生成析构函数
    virtual void SyncLog(const std::string logmessage) = 0;//必须要重写这个虚函数
};

class ConsoleLogStrategy : public LogStrategy
{
public:
    virtual ~ConsoleLogStrategy()
    {}

    virtual void SyncLog(const std::string logmessage) override
    {
        //向显示器写入日志信息，显示器为临界资源，需要保护
        {
            LockGuard lockguard(&_lock);
            std::cout << logmessage << std::endl;
        }
    }
private:
    Mutex _lock;
};

const std::string dirpathnamedefault = "log";
const std::string filenamedefault = "test.log";

class EnableFileLogStrategy : public LogStrategy
{
public:
    EnableFileLogStrategy(const std::string dirpathname = dirpathnamedefault,
    const std::string filename = filenamedefault)
    :_dir_path_name(dirpathname)
    ,_filename(filename)
    {
        //构造的时候检查此目录是否存在 ,利用filesystem
        {
            LockGuard lockguard(&_lock);
            if(std::filesystem::exists(_dir_path_name)) 
            {
                return;
            }
            //没有就需要创建目录，但是创建目录的过程可能会失败，就需要去捕获异常
            try
            {
                std::filesystem::create_directories(_dir_path_name);
            }
            catch(const std::filesystem::filesystem_error& e)
            {
                std::cerr << e.what() << '\n';
            }
        }
    }
    virtual ~EnableFileLogStrategy()
    {}
    virtual void SyncLog(const std::string logmessage) override
    {
        {
            LockGuard lockguard(&_lock);
            //拼接创建文件的路径
            std::string target = _dir_path_name;
            target +='/';
            target += _filename;

            //创建文件，向文件中写入、关闭文件 -> 利用文件流 ofstream
            std::ofstream out(target.c_str() , std::ios::app);//追加的形式打开文件
            if(!out.is_open())
            {
                return;
            }
            //向文件中写入
            out << logmessage << "\n";
            out.close();
        }
    }
private:
    Mutex _lock;
    //将日志文件放在目录下
    std::string _dir_path_name;
    std::string _filename;
};

//获取当前时间 - 利用时间戳
std::string GetCurrentTime()
{
    //获取当前时间戳 -> time
    time_t currtime = time(nullptr);//参数为时区，已经被设置好了，填写nullptr 就可以了

    //从时间戳中获取具体的时间信息
    struct tm currtm;
    localtime_r(&currtime ,&currtm);

    //转换为字符串并返回 --> snprintf
    char timebuffer[64];
    snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
            currtm.tm_year + 1900,
            currtm.tm_mon+1,
            currtm.tm_mday,
            currtm.tm_hour,
            currtm.tm_min,
            currtm.tm_sec);
    return timebuffer;
}

//处理整条日志以及将日志刷新出去
class Logger
{
public:
    //构造
    Logger()
    {}
    ~Logger()
    {}
    //刷新策略
    void EnableConsoleLogStrategy()
    {
        _strategy = std::make_unique<ConsoleLogStrategy>();
    }
    void EnbaleFileLogStrategy()
    {
        _strategy = std::make_unique<EnableFileLogStrategy>();
    }
    //内部类，打印整条的日志信息
class LogMessage
{
public:
    LogMessage(LogLevel level , std::string filename , int line  ,Logger& logger)
    :_currtime(GetCurrentTime())
    ,_level(level)
    ,_pid(getpid())
    ,_filename(filename)
    ,_line(line)
    ,_logger(logger)
    {
        //拼接 , 利用stringstream
        std::stringstream ss;
        ss << '[' << _currtime <<"] "
            << '[' << Level2String(_level) << "] "
            <<"[" << _pid << "] "
            <<"[" << _filename <<"] "
            <<"[" << _line << "] "
            << " - ";
        _loginfo = ss.str();
    }

    //未来打印日志
    //LOG(level) << "hahah" << 20; --> 重载 , 数据参数不确定->使用模板
    template<class T>
    LogMessage& operator<<(const T& info)
    {
        std::stringstream ss;
        ss << info;
        _loginfo += ss.str();
        return *this;
    }

    //临时对象结束的时候自动做刷新动作
    ~LogMessage()
    {
        if(_logger._strategy)//指定了刷新方法才做刷新
        {
            _logger._strategy->SyncLog(_loginfo);
        }
    }
private:
    //[时间] [日志等级] [进程pid] [文件名] [行号]-可变参数
    std::string _currtime;
    LogLevel _level;
    pid_t _pid;
    std::string _filename;
    int _line;

    //整条日志信息
    std::string _loginfo;
    Logger& _logger;//提供刷新策略
};

    //未来打印日志
    //LOG(level) << "hahah" << 20; --> 重载 , 数据参数不确定->使用模板
    LogMessage operator()(LogLevel level , std::string filename , int line)
    {   
        return LogMessage(level , filename , line, *this);
    }
private:
    //刷新策略，使用指针指针指向基类
    std::unique_ptr<LogStrategy> _strategy;
};

Logger logger;//定义全局的Logger 对象
#define LOG(level) logger(level ,__FILE__, __LINE__)
#define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
#define EnableFileLogStrategy() logger.EnbaleFileLogStrategy()